package com.xu.cloud.auth.component.impl;

import com.xu.cloud.auth.component.TokenComponent;
import com.xu.cloud.auth.constant.AuthConstant;
import com.xu.cloud.auth.exception.AuthErrorCodeEnum;
import com.xu.cloud.common.auth.AuthContext;
import com.xu.cloud.common.constant.CryptConstant;
import com.xu.cloud.common.constant.PrefixConstant;
import com.xu.cloud.common.exception.ErrorCodeEnum;
import com.xu.cloud.common.sequence.ISequence;
import com.xu.cloud.common.sequence.wrapper.DateTimeFormatSequenceWrapper;
import com.xu.cloud.common.util.AssertUtil;
import com.xu.cloud.common.util.JsonUtil;
import com.xu.cloud.common.util.MessageDigestUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * token组件实现类
 *
 * @author xuguan
 * @since 2025/2/23
 */
@Slf4j
@Component
public class TokenComponentImpl implements TokenComponent {
    @Autowired
    private ISequence sequence;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public String createAccessToken(AuthContext authContext) {
        AssertUtil.assertNotNull(authContext);
        final Long userId = authContext.getUserId();
        AssertUtil.assertNotNull(userId);
        final ValueOperations<String, String> vop = stringRedisTemplate.opsForValue();

        final DateTimeFormatSequenceWrapper sw = new DateTimeFormatSequenceWrapper(sequence);
        final String accessToken = MessageDigestUtil.encryptToHex(userId + sw.nextId(), CryptConstant.MD5_DIGEST_ALGORITHM);
        vop.set(PrefixConstant.XU_CLOUD_AUTH_REDIS_PREFIX + accessToken,
                JsonUtil.toJsonString(authContext),
                AuthConstant.ACCESS_TOKEN_EXPIRE_TIME_DAYS,
                TimeUnit.DAYS);

        log.info("创建access_token完成, accessToken={}", accessToken);
        return accessToken;
    }

    @Override
    public AuthContext resolveAccessToken(@Nullable String accessToken, boolean delete) {
        log.info("解析accessToken开始, accessToken={}", accessToken);
        AssertUtil.assertHasText(accessToken, ErrorCodeEnum.UNAUTHENTICATED.info().newException());

        final String accessTokenKey = PrefixConstant.XU_CLOUD_AUTH_REDIS_PREFIX + accessToken;

        // 检查access_token是否过期
        AssertUtil.assertTrue(Boolean.TRUE.equals(stringRedisTemplate.hasKey(accessTokenKey)),
                ErrorCodeEnum.UNAUTHENTICATED.info().newException());

        // 获取access_token缓存信息
        final ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
        final String authContextValue = valueOperations.get(accessTokenKey);

        if (delete) {
            // 获取完删除access_token
            stringRedisTemplate.delete(accessTokenKey);
        }

        // 检查缓存客户信息
        AuthContext authContext;
        try {
            authContext = JsonUtil.parseObj(authContextValue, AuthContext.class);
        } catch (Exception e) {
            throw AuthErrorCodeEnum.ILLEGAL_TOKEN.info().newException();
        }
        AssertUtil.assertNotNull(authContext, AuthErrorCodeEnum.ILLEGAL_TOKEN.info().newException());
        AssertUtil.assertNotNull(authContext.getUserId(), AuthErrorCodeEnum.ILLEGAL_TOKEN.info().newException());

        log.info("解析accessToken结束, authContext={}", authContext);
        return authContext;
    }

    @Override
    public String refreshAccessToken(String accessToken) {
        log.info("刷新accessToken开始, 原accessToken={}", accessToken);
        final AuthContext authContext = resolveAccessToken(accessToken, true);
        final String newAccessToken = createAccessToken(authContext);
        log.info("刷新accessToken结束, 新accessToken={}", newAccessToken);
        return newAccessToken;
    }

    @Override
    public void extendAccessToken(String accessToken) {
        log.info("续期access_token开始, accessToken={}", accessToken);
        final String accessTokenKey = PrefixConstant.XU_CLOUD_AUTH_REDIS_PREFIX + accessToken;
        if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(accessTokenKey))) {
            log.info("access_token已过期");
            return;
        }
        stringRedisTemplate.expire(accessTokenKey, AuthConstant.ACCESS_TOKEN_EXPIRE_TIME_DAYS, TimeUnit.DAYS);
        log.info("续期access_token成功");
    }

    @Override
    public void deleteAccessToken(String accessToken) {
        log.info("删除accessToken开始, accessToken={}", accessToken);
        AssertUtil.assertHasText(accessToken);
        final String accessTokenKey = PrefixConstant.XU_CLOUD_AUTH_REDIS_PREFIX + accessToken;
        stringRedisTemplate.delete(accessTokenKey);
        log.info("删除accessToken成功");
    }

    @Override
    public void checkMessageCodeToken(String messageCodeToken, String mobileNo) {
        log.info("校验messageCodeToken开始, messageCodeToken={}, mobileNo={}", messageCodeToken, mobileNo);
        AssertUtil.assertHasText(messageCodeToken);
        final String messageTokenKey = PrefixConstant.XU_CLOUD_MESSAGE_TOKEN_REDIS_PREFIX + messageCodeToken;
        // token是否过期
        AssertUtil.assertTrue(Boolean.TRUE.equals(stringRedisTemplate.hasKey(messageTokenKey)),
                AuthErrorCodeEnum.MESSAGE_CODE_EXPIRE.info().newException());

        // 获取缓存手机号
        final ValueOperations<String, String> vop = stringRedisTemplate.opsForValue();
        final String redisValue = vop.get(messageTokenKey);

        // 获取完删除短信token
        stringRedisTemplate.delete(messageTokenKey);

        // 校验缓存手机号是否一致
        AssertUtil.assertEquals(mobileNo, redisValue,
                AuthErrorCodeEnum.MESSAGE_CODE_INVALID.info().newException());

        log.info("校验messageCodeToken成功");
    }
}
